// Author: Domien Nowicki

#include "WorldEditorWidget.h"
#include "ToolboxWidget.h"
#include <QFileDialog>
#include <QColorDialog>
#include <QMessageBox>

#include "CameraWidget.h"

WorldEditorWidget::WorldEditorWidget(QWidget *parent): QWidget(parent), World(new MapWidget(this), new TextureManagerModel(this)), currentTextureIndex(-1)
{
	SetupWidget();
}

WorldEditorWidget::~WorldEditorWidget()
{
}

void WorldEditorWidget::SetupWidget()
{
	eraserCursor = QCursor(QPixmap("icons/erasercursor.png"), 3, 0);
	wallCursor = QCursor(QPixmap("icons/wallcursor.png"), 0, 0);
	textureCursor = QCursor(QPixmap("icons/texturecursor.png"), 0, 0);
	cameraCursor = QCursor(QPixmap("icons/cameracursor.png"), 2, 0);

	// Setup camera widget
	cameraWidget = new CameraWidget(this);
	cameraWidget->hide();
	QObject::connect(cameraWidget, SIGNAL(clicked()), this, SLOT(slotCameraClicked()));
	QObject::connect(cameraWidget, SIGNAL(rotated(const Scalar&)), this, SLOT(slotCameraRotated(const Scalar&)));

	vboxLayout = new QVBoxLayout();
	hboxLayout = new QHBoxLayout();

	// Setup texture manager dialog
	textureDialog = new TextureManagerDialog(this);
	textureDialog->SetTextureManagerModel( (TextureManagerModel*)textures );

	QObject::connect(textureDialog, SIGNAL(removeTexture(unsigned int)), this, SLOT(slotRemoveTexture(unsigned int)));
	QObject::connect(textureDialog, SIGNAL(addTexture()), this, SLOT(slotAddTexture()));
	QObject::connect(textureDialog, SIGNAL(addSolidTexture()), this, SLOT(slotAddSolidTexture()));
	QObject::connect(textureDialog, SIGNAL(changeUserColor(unsigned int, const Pixel&)), this, SLOT(slotChangeUserColor(unsigned int, const Pixel&)));


	setLayout(hboxLayout);

	// Setup toolbox widget
	toolboxWidget = new ToolboxWidget(this);

	QObject::connect(toolboxWidget, SIGNAL(clickedTool(ToolboxWidget::Tool)), this, SLOT(slotToolClicked(ToolboxWidget::Tool)));
	QObject::connect(toolboxWidget, SIGNAL(clickedTextureList()), this, SLOT(slotTextureListClicked()));

	hboxLayout->addWidget(toolboxWidget);

	hboxLayout->addLayout(vboxLayout);

	// Setup default textures widget
	defaultTextureWidget = new DefaultTextureWidget(this);
	UpdateDefaultTextures();

	QObject::connect(defaultTextureWidget, SIGNAL(changeTexture(DefaultTextureWidget::DefaultTexture)), this, SLOT(slotChangeDefaultTexture(DefaultTextureWidget::DefaultTexture)));

	vboxLayout->addWidget(defaultTextureWidget);

	// Add cameraWidget to the map, but cameraWidget is hidden initially
	MapWidget *mapWidget = GetMapWidget();
	mapWidget->scene()->addItem(cameraWidget);

	// Setup mapWidget connections
	QObject::connect(mapWidget, SIGNAL(clickedCell(unsigned int, unsigned int)), this, SLOT(slotCellClicked(unsigned int, unsigned int)));
	QObject::connect(mapWidget, SIGNAL(clickedCellSide(unsigned int, unsigned int, Cell::Side)), this, SLOT(slotCellSideClicked(unsigned int, unsigned int, Cell::Side)));
	vboxLayout->addWidget(mapWidget);
}

// Action to take when a cell was clicked
void WorldEditorWidget::slotCellClicked(unsigned int mapX, unsigned int mapY)
{
	switch(toolboxWidget->GetToolType())
	{
		case ToolboxWidget::CAMERA:
		{
			PlaceCamera(Vector( (double)mapX+0.5f, (double)mapY+0.5f), 0);
		}
		break;

		case ToolboxWidget::WALL:
		{
			SetWallCell(mapX, mapY);
		}
		break;

		case ToolboxWidget::ERASE:
		{
			SetEmptyCell(mapX, mapY);
		}
		break;

		default:
			break;
	}
}

// Action to take when a cell side was clicked
void WorldEditorWidget::slotCellSideClicked(unsigned int mapX, unsigned int mapY, Cell::Side side)
{
	switch(toolboxWidget->GetToolType())
	{
		case ToolboxWidget::TEXTURE:
		{
			SetTextureCell(mapX, mapY, side, currentTextureIndex);
		}
		break;

		case ToolboxWidget::ERASE:
		{
			SetTextureCell(mapX, mapY, side, -1);
		}
		break;


		default:
			// If nothing specified, use the default cell clicked action
			slotCellClicked(mapX, mapY);
			break;
	}
}

void WorldEditorWidget::slotTextureListClicked()
{
	// Clicked on "Select texture"  in dialog?
	if (QDialog::Accepted == textureDialog->exec())
	{
		currentTextureIndex = textureDialog->GetCurrentTextureIndex();

		// Selected empty texture?
		if (-1 == currentTextureIndex)
		{
			ResetSelectedTexture();
		}
		else
		{
			// Otherwise set the current texture to the given thumb
			toolboxWidget->SetCurrentTexture( textureDialog->GetTextureThumb(currentTextureIndex) );
		}
	}
}

// Tool button clicked?
void WorldEditorWidget::slotToolClicked(enum ToolboxWidget::Tool toolType)
{
	MapWidget *mapWidget = GetMapWidget();


	cameraWidget->DisableRotation();

	// Set new cursor for specific tool
	switch(toolType)
	{
		case ToolboxWidget::CAMERA:
		{
			mapWidget->SetCursor(cameraCursor);

			// And only enable rotation when camera tool is clicked
			cameraWidget->EnableRotation();
		}
		break;

		case ToolboxWidget::TEXTURE:
		{
			mapWidget->SetCursor(textureCursor);
		}
		break;

		case ToolboxWidget::ERASE:
		{
			mapWidget->SetCursor(eraserCursor);
		}
		break;

		case ToolboxWidget::WALL:
		{
			mapWidget->SetCursor(wallCursor);
		}
		break;

		default:
			break;
	}
}



MapWidget *WorldEditorWidget::GetMapWidget()
{
	return (MapWidget*)map;
}

void WorldEditorWidget::Reset()
{
	World::Reset();
	ResetSelectedTexture();
	UpdateDefaultTextures();
}

void WorldEditorWidget::slotAddTexture()
{
	// Ask which file to load
	QString qFilename = QFileDialog::getOpenFileName(this, tr("Select Image File"), "", tr("Images (*.bmp *.png *.gif *.jpg *.jpeg *.pcx *.pbm *.ppm *.tiff)"));

	// Valid filename?
	if (qFilename.length())
	{
		try
		{
			AddTexture(Texture(qFilename.toStdString()));
		}
		catch(std::runtime_error e)
		{
			QMessageBox::critical(this, tr("Error"), e.what());	
		}
		catch(...)
		{
			QMessageBox::critical(this, tr("Error"), "Sorry, an error occurred while loading the image.");	
		}
	}
}


void WorldEditorWidget::slotAddSolidTexture()
{
	// Ask which color to add
	QColor color = QColorDialog::getColor(Qt::white, this);

	if (color.isValid())
	{
		Pixel pixel(color.red(), color.green(), color.blue());

		AddTexture(Texture(pixel));
	}
}

void WorldEditorWidget::slotRemoveTexture(unsigned int tindex)
{
	RemoveTexture(tindex);

	// If selected texture was removed, reset to empty texture
	if (tindex == currentTextureIndex)
	{
		ResetSelectedTexture();
	}

	// Also calculate new texture index for currently selected texture
	currentTextureIndex = CalculateTextureIndex(tindex, currentTextureIndex);

	UpdateDefaultTextures();
}

void WorldEditorWidget::ResetSelectedTexture()
{
	toolboxWidget->SetEmptyTexture();
	currentTextureIndex = -1;
}

// Action to take when user wants to change a default texture
void WorldEditorWidget::slotChangeDefaultTexture(DefaultTextureWidget::DefaultTexture texture)
{
	
	if (QDialog::Accepted == textureDialog->exec())
	{
		unsigned int tindex = textureDialog->GetCurrentTextureIndex();

		// Show its new texture thumb
		defaultTextureWidget->SetDefaultTexture(texture, textureDialog->GetTextureThumb(tindex));

		// And actually set the new texture index
		switch(texture)
		{
			case DefaultTextureWidget::DEFAULT:
			{
				SetDefaultTexture(tindex);
			}
			break;

			case DefaultTextureWidget::FLOOR:
			{
				SetFloorTexture(tindex);
			}
			break;

			case DefaultTextureWidget::CEILING:
			{
				SetCeilingTexture(tindex);
			}
			break;
		}
	}
}

// Action to take when eraser tool clicked on the camera widget
void WorldEditorWidget::slotCameraClicked()
{
	switch(toolboxWidget->GetToolType())
	{
		case ToolboxWidget::ERASE:
		{
			RemoveCamera();
		}
		break;
	}
}

void WorldEditorWidget::slotCameraRotated(const Scalar& angle)
{
	// Place camera, but with a different angle
	PlaceCamera( GetCameraPos(), angle );
}

void WorldEditorWidget::LoadFromFile(const std::string& fileName)
{
	World::LoadFromFile(fileName);
	ResetSelectedTexture();

	UpdateDefaultTextures();
}

void WorldEditorWidget::UpdateDefaultTextures()
{
	defaultTextureWidget->SetDefaultTexture(DefaultTextureWidget::DEFAULT, textureDialog->GetTextureThumb(GetDefaultTextureIndex()));
	defaultTextureWidget->SetDefaultTexture(DefaultTextureWidget::FLOOR, textureDialog->GetTextureThumb(GetFloorTextureIndex()));
	defaultTextureWidget->SetDefaultTexture(DefaultTextureWidget::CEILING, textureDialog->GetTextureThumb(GetCeilingTextureIndex()));
}

void WorldEditorWidget::SetChanged()
{
	World::SetChanged();

	emit changed();
}

void WorldEditorWidget::SetUnchanged()
{
	World::SetUnchanged();

	emit changed();
}

void WorldEditorWidget::PlaceCamera(const Vector& pos, const Scalar& angle)
{
	World::PlaceCamera(pos, angle);

	MapWidget *mapWidget = GetMapWidget();
	
	// We only show the camera if the world has a camera placed
	if (HasPlacedCamera())
	{
		// Offset inside a cell
		int offsetX = (mapWidget->GetCellWidgetSize() - cameraWidget->GetWidth())/2;
		int offsetY = (mapWidget->GetCellWidgetSize() - cameraWidget->GetHeight())/2;

		// Offset to map x and y
		int mapX = GetCameraPos().GetIntX();
		int mapY = GetCameraPos().GetIntY();

		cameraWidget->setPos(mapX*mapWidget->GetCellWidgetSize() + offsetX, mapY*mapWidget->GetCellWidgetSize() + offsetY);
		cameraWidget->SetAngle(GetCameraAngle());
		cameraWidget->show();
	}
}

void WorldEditorWidget::RemoveCamera()
{
	World::RemoveCamera();

	cameraWidget->hide();
}

void WorldEditorWidget::slotChangeUserColor(unsigned int tindex, const Pixel& userColor)
{
	ChangeUserColor(tindex, userColor);
}